// <copyright file="DynamicCode.cs" company="Signal Processing Lab, Wuhan University">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>Xinping Deng</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

// ********************************************************
//
// Jamie Shotton
// Machine Intelligence Laboratory
// Department of Engineering
// University of Cambridge, UK
// Copyright (c) 2006
// All Rights Reserved.
//
//  Permission is hereby granted, free of charge, to use and distribute
//  this software and its documentation without restriction, including
//  without limitation the rights to use, copy, modify, merge, publish,
//  distribute, sublicense, and/or sell copies of this work, and to
//  permit persons to whom this work is furnished to do so, subject to
//  the following conditions:
//   1. The code must retain the above copyright notice, this list of
//      conditions and the following disclaimer.
//   2. Any modifications must be clearly marked as such.
//   3. Original authors' names are not deleted.
//   4. The authors' names are not used to endorse or promote products
//      derived from this software without specific prior written
//      permission.
//
//  THE UNIVERSITY OF CAMBRIDGE AND THE CONTRIBUTORS TO THIS WORK
//  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
//  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
//  SHALL THE UNIVERSITY OF CAMBRIDGE NOR THE CONTRIBUTORS BE LIABLE
//  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
//  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
//  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
//  THIS SOFTWARE.
//
// ********************************************************
//                      Author :  Jamie Shotton
//                      Date   :  May 2006
//  This work pertains to the research described in the ECCV 2006 paper
//  TextonBoost: Joint Appearance, Shape and Contex Modeling
//  for Multi-Class Object Recognition and Segmentation
// ********************************************************

namespace SPLWHU.Core.Raster
{
    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text;
    using System.Text.RegularExpressions;
    using Microsoft.CSharp;

    /// <summary>
    /// The dynamic code. Generates class dynamically according to the inputs.
    /// </summary>
    public class DynamicCode
    {
        /// <summary>
        /// The lock object. To lock the operation.
        /// </summary>
        private static readonly object lockObject = new object();

        /// <summary>
        /// The CSharp compiler.
        /// </summary>
        private static CodeDomProvider compiler = null;

        /// <summary>
        /// The invoke object. An instance of this class.
        /// </summary>
        private object invokerObject;

        /// <summary>
        /// The compiler parameters.
        /// </summary>
        private CompilerParameters compilerParameters;

        /// <summary>
        /// The additional using statement.
        /// </summary>
        private string usingStatement = string.Empty;

        /// <summary>
        /// Adds a reference assembly.
        /// </summary>
        /// <param name="path">The path of the reference.</param>
        public void AddReference(string path)
        {
            try
            {
                this.compilerParameters.ReferencedAssemblies.Add(path);
            }
            catch (Exception ex)
            {
                throw new OperatorException(ex.Message);
            }
        }

        /// <summary>
        /// Adds a namespace.
        /// </summary>
        /// <param name="namesp">The namespace to add.</param>
        public void AddNamespace(string namesp)
        {
            this.usingStatement = this.usingStatement + "using " + namesp + ";\n";
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DynamicCode"/> class.
        /// </summary>
        public DynamicCode()
        {
            // Create a compiler
            if (compiler == null)
            {
                compiler = CodeDomProvider.CreateProvider("CSharp");
            }

            // Initialise compiler parameters
            this.compilerParameters = new CompilerParameters();
            this.compilerParameters.GenerateInMemory = true;
            this.compilerParameters.ReferencedAssemblies.Add("System.dll");
            this.compilerParameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
        }

        /// <summary>
        /// Compiles the dynamic code.
        /// </summary>
        /// <param name="types">The types.</param>
        /// <param name="arguments">The arguments.</param>
        /// <param name="codeBody">The code body.</param>
        public void Compile(string[] types, string arguments, string codeBody)
        {
            lock (lockObject)
            {
                // Replaces the type names.
                int cnt = types.Length;
                for (int i = 0; i < cnt; i++)
                {
                    arguments = Regex.Replace(arguments, "T" + i.ToString(), types[0]);
                    codeBody = Regex.Replace(codeBody, "T" + i.ToString(), types[0]);
                }

                // Generate a complete source code string
                string code = @"
using System;
using SPLWHU.Core;
"+this.usingStatement+@"

namespace DynamicCodeNamespace
{
    public class DynamicCode
    {
        public object DynamicCodeFunction(" + arguments + @")
        {
            " + codeBody + @"
            return null;
        }
    }
}
";

                // Compiles the code.
                CompilerResults compiled = compiler.CompileAssemblyFromSource(compilerParameters, code);

                // Catches the errors.
                if (compiled.Errors.HasErrors)
                {
                    string errString = string.Empty;
                    errString += compiled.Errors.Count + " Errors:";
                    for (int i = 0; i < compiled.Errors.Count; i++)
                    {
                        errString += "\r\nLine: " + compiled.Errors[i].Line + " - " + compiled.Errors[i].ErrorText;
                    }

                    throw new OperatorException(errString);
                }

                // Creates a new instance.
                this.invokerObject = compiled.CompiledAssembly.CreateInstance("DynamicCodeNamespace.DynamicCode");
                if (this.invokerObject == null)
                {
                    throw new Exception("Could not create copy object!");
                }
            }
        }

//        /// <summary>
//        /// Initializes a new instance of the <see cref="DynamicCode"/> class.
//        /// </summary>
//        /// <param name="typeInput">The input type.</param>
//        /// <param name="typeOutput">The output type.</param>
//        /// <param name="arguments">The arguments.</param>
//        /// <param name="codeBody">The code body.</param>
//        public DynamicCode(string typeInput, string typeOutput, string arguments, string codeBody)
//        {
//            lock (lockObject)
//            {
//                // Create a compiler
//                if (compiler == null)
//                {
//                    compiler = CodeDomProvider.CreateProvider("CSharp");
//                }

//                // Initialise compiler parameters
//                compilerParameters = new CompilerParameters();
//                compilerParameters.GenerateInMemory = true;
//                compilerParameters.ReferencedAssemblies.Add("System.dll");
//                compilerParameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);

//                // Find and replace typeNames for tin, tout, etc in arguments and codeBody
//                arguments = Regex.Replace(arguments, "tin", typeInput);
//                arguments = Regex.Replace(arguments, "tout", typeOutput);

//                codeBody = Regex.Replace(codeBody, "tin", typeInput);
//                codeBody = Regex.Replace(codeBody, "tout", typeOutput);

//                // Generate a complete source code string
//                string code = @"
//using System;
//using SPLWHU.Core;
//
//namespace DynamicCodeNamespace
//{
//    public class DynamicCode
//    {
//        public object DynamicCodeFunction(" + arguments + @")
//        {
//            " + codeBody + @"
//            return null;
//        }
//    }
//}
//";

//                // Compiles the code.
//                CompilerResults compiled = compiler.CompileAssemblyFromSource(compilerParameters, code);

//                // Catches the errors.
//                if (compiled.Errors.HasErrors)
//                {
//                    string errString = string.Empty;
//                    errString += compiled.Errors.Count + " Errors:";
//                    for (int i = 0; i < compiled.Errors.Count; i++)
//                    {
//                        errString += "\r\nLine: " + compiled.Errors[i].Line + " - " + compiled.Errors[i].ErrorText;
//                    }

//                    throw new Exception(errString);
//                }

//                // Creates a new instance.
//                this.invokerObject = compiled.CompiledAssembly.CreateInstance("DynamicCodeNamespace.DynamicCode");
//                if (this.invokerObject == null)
//                {
//                    throw new Exception("Could not create copy object!");
//                }
//            }
//        }

        /// <summary>
        /// Invokes the method in this instance.
        /// </summary>
        /// <param name="arguments">The arguments.</param>
        /// <returns>The computing results.</returns>
        public object Invoke(params object[] arguments)
        {
            try
            {
                return this.invokerObject.GetType().InvokeMember("DynamicCodeFunction", BindingFlags.InvokeMethod, null, this.invokerObject, arguments);
            }
            catch (TargetInvocationException tie)
            {
                throw tie.InnerException;
            }
        }
    }
}
